from locust import HttpUser, task, between, events
from locust.exception import StopUser
import websocket
import threading
import time
import json
import logging
import uuid
import random
import struct
import math
from net_class import *

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

global_count = 1

class GameUser(HttpUser):
    wait_time = between(1, 3)
    host = "https://cdn.xy.siyuf.com"
    server_list_url = "https://gameapi.xy.siyuf.com/"  # 服务器列表地址
    ws_base_url = "wss://game1.xy.siyuf.com/"  # WebSocket基础地址
    ws = None
    connected = False
    user_id = None
    request_id = 0
    server_list = []  # 存储服务器列表
    selected_server = None  # 选中的服务器

    def on_start(self):
        """用户开始时执行，按顺序执行完整流程"""
        # self.user_id = "stresstest" + str(uuid.uuid4())
        # random.randint(1, 100)
        # self.user_id = "stresstest" + str(int(time.time() * 1000)) + "_" + str(random.randint(1, 100))
        self.is_enter_game = False
        self.LastPingTime = 0
        self.recv_buffers = []
        self.pos_vector = [203, 82]

        global global_count
        # self.user_id = f"zxk00{global_count}"
        self.user_id = f"stresstest_{global_count}"
        global_count += 1
        logger.info(f"用户 {self.user_id} 开始测试流程")
        
        try:
            # 1. 访问初始游戏页面
            self.visit_initial_page()
            
            # 2. 获取最新的区服
            self.fetch_last_server_list()

            # 2. 获取服务器列表
            self.fetch_server_list()

            # 3. 选择服务器
            self.select_server()

            # 3. 获取更新公告
            self.fetch_server_notice_list()

            # 2. 登录服务器
            self.fetch_login_server()
            
            # 4. 建立WebSocket连接
            self.connect_websocket()
            
        except Exception as e:
            logger.error(f"用户 {self.user_id} 初始化流程失败: {str(e)}")
            raise StopUser()

    def visit_initial_page(self):
        """访问初始游戏页面"""
        logger.info(f"用户 {self.user_id} 访问初始页面")
        response = self.client.get(
            "/XYres/index_waiwang.html?isPCWeiduan=true",
            name="访问初始页面"
        )
        if response.status_code != 200:
            logger.error(f"用户 {self.user_id} 访问初始页面失败，状态码: {response.status_code}")
            raise Exception("初始页面访问失败")
        else :
            self.client_cookies = self.client.cookies

    def fetch_last_server_list(self):
        """获取最新的区服"""
        logger.info(f"用户 {self.user_id} 获取最新的区服")
        try:
            # 发送GET请求获取服务器列表
            serverurl = f"{self.server_list_url}/api/v1/server/get_last_server?platform_id=1&account={self.user_id}"
            response = self.client.get(
                serverurl,
                name="获取最新的区服"
            )
            
            if response.status_code != 200:
                logger.error(f"用户 {self.user_id} 获取最新的区服失败，状态码: {response.status_code}")
                raise Exception("服务器列表获取失败")
            
            # 解析服务器列表（根据实际返回格式调整）
            result = response.json()

            if result == []: # 可能返回空数组
                logger.error(f"用户 {self.user_id} 获取最新的区服失败，返回空数据")
                return
            self.isFirstLogin = result.get("isFirstLogin")

            # 2. 检查响应状态（根据 code 判断是否成功）
            if result.get("code") == 200:
                # 3. 提取 lastlogin_server 信息
                last_server = result.get("lastlogin_server", {})
                
                # 4. 获取具体字段值
                server_no = last_server.get("no")  # 服务器编号：5
                server_name = last_server.get("name")  # 服务器名称："侠客5区"
                server_address = last_server.get("address")  # 服务器地址："game1.xy.siyuf.com:443"
                is_maintenance = last_server.get("isMaintenance")  # 是否维护：false
                
                # 打印提取的信息（调试用）
                print(f"获取到服务器信息：{server_name}（编号：{server_no}）")
                print(f"服务器地址：{server_address}")
                
                # 5. 根据服务器地址构建 WebSocket 连接（示例）
                # 从 address 中提取域名（去掉端口）
                # ws_domain = server_address.split(":")[0]  # 得到 "game1.xy.siyuf.com"
                # self.ws_url = f"wss://{ws_domain}/"  # 构建 wss 地址
                # self.ws_port = server_address.split(":")[1]

                self.server_no = server_no
                self.server_name = server_name
                self.ws_url = f"wss://{server_address}/"
                
            else:
                print(f"接口返回错误，code：{result.get('code')}")
                raise Exception("获取最新的区服失败")
            
        except json.JSONDecodeError:
            logger.error(f"用户 {self.user_id} 获取最新的区服响应格式不正确")
            raise Exception("获取最新的区服格式错误")
        except Exception as e:
            logger.error(f"用户 {self.user_id} 获取最新的区服异常: {str(e)}")
            raise
    
    def fetch_server_notice_list(self):
        """获取更新公告"""
        logger.info(f"用户 {self.user_id} 获取更新公告")
        try:
            # 发送GET请求获取服务器列表
            serverurl = f"{self.server_list_url}/api/v1/notice/login?productSlug=1&platformId=2&channelId=1"
            response = self.client.get(
                serverurl,
                name="获取更新公告"
            )
            
            if response.status_code != 200:
                logger.error(f"用户 {self.user_id} 获取获取更新公告失败，状态码: {response.status_code}")
                raise Exception("服务器列表获取失败")
            
        except json.JSONDecodeError:
            logger.error(f"用户 {self.user_id} 获取获取更新公告应格式不正确")
            raise Exception("获取获取更新公告格式错误")
        except Exception as e:
            logger.error(f"用户 {self.user_id} 获取获取更新公告异常: {str(e)}")
            raise

    def fetch_login_server(self):
        """登录服务器"""
        logger.info(f"用户 {self.user_id} 登录服务器")
        try:
            # 发送GET请求 登录服务器
            serverurl = f"{self.server_list_url}/api/v1/ban/account_disable?agentid=1&openId={self.user_id}"
            response = self.client.get(
                serverurl,
                name="登录服务器"
            )
            
            if response.status_code != 200:
                logger.error(f"用户 {self.user_id} 获取最新的区服失败，状态码: {response.status_code}")
                raise Exception("服务器列表获取失败")
            
            # 解析服务器列表（根据实际返回格式调整）
            result = response.json()

            if result.get("code") == 0:
                logger.info(f"用户 {self.user_id} 成功登录服务器")

            else:
                logger.error(f"用户 {self.user_id} 登录服务器失败")
                raise Exception("登录服务器失败")
        except json.JSONDecodeError:
            logger.error(f"用户 {self.user_id} 获取登录服务器响应格式不正确")
            raise Exception("获取登录服务器格式错误")
        except Exception as e:
            logger.error(f"用户 {self.user_id} 登录服务器异常: {str(e)}")
            raise

    def fetch_server_list(self):
        """获取服务器列表"""
        logger.info(f"用户 {self.user_id} 请求服务器列表")
        try:
            # 发送GET请求获取服务器列表
            serverurl = f"{self.server_list_url}/api/v1/server/get_server_list?productSlug=1&account={self.user_id}"
            response = self.client.get(
                serverurl,
                name="获取服务器列表"
            )
            
            if response.status_code != 200:
                logger.error(f"用户 {self.user_id} 获取服务器列表失败，状态码: {response.status_code}")
                raise Exception("服务器列表获取失败")
            
            # 解析服务器列表（根据实际返回格式调整）
            result = response.json()
            self.server_list = result.get("list", [])
            
            if not self.server_list:
                logger.error(f"用户 {self.user_id} 未获取到有效服务器列表")
                raise Exception("未获取到服务器列表")
                
            logger.info(f"用户 {self.user_id} 成功获取 {len(self.server_list)} 个服务器")
            
        except json.JSONDecodeError:
            logger.error(f"用户 {self.user_id} 服务器列表响应格式不正确")
            raise Exception("服务器列表格式错误")
        except Exception as e:
            logger.error(f"用户 {self.user_id} 获取服务器列表异常: {str(e)}")
            raise

    def select_server(self):
        """选择一个服务器（这里随机选择一个）"""
        if not self.server_list:
            raise Exception("没有可用服务器列表")
            
        # 随机选择一个服务器（可以根据负载情况选择，这里简化为随机）
        self.mSelectServer = self.server_list[0]

        # 4. 获取具体字段值
        server_no = self.mSelectServer.get("no")  # 服务器编号：5
        server_name = self.mSelectServer.get("name")  # 服务器名称："侠客5区"
        server_address = self.mSelectServer.get("address")  # 服务器地址："game1.xy.siyuf.com:443"
        is_maintenance = self.mSelectServer.get("isMaintenance")  # 是否维护：false
        
        # 打印提取的信息（调试用）
        print(f"获取到服务器信息：{server_name}（编号：{server_no}）")
        print(f"服务器地址：{server_address}")
        
        # 5. 根据服务器地址构建 WebSocket 连接（示例）
        # 从 address 中提取域名（去掉端口）
        # ws_domain = server_address.split(":")[0]  # 得到 "game1.xy.siyuf.com"
        # self.ws_url = f"wss://{ws_domain}/"  # 构建 wss 地址
        # self.ws_port = server_address.split(":")[1]

        self.ws_url = f"wss://{server_address}/"
        self.server_no = server_no
        self.server_name = server_name
        
        logger.info(f"用户 {self.user_id} 选择服务器: {self.mSelectServer.get('no', '未知ID')} - {self.mSelectServer.get('name', '未知名称')}")

    def connect_websocket(self):
        """根据选中的服务器建立WebSocket连接"""
        # if not self.selected_server:
        #     raise Exception("未选择服务器")
            
        # time.sleep(0.1)

        try:
            # 构建完整的WebSocket地址（如果需要包含服务器ID等参数）
            # 根据实际情况调整，可能需要从选中的服务器信息中获取ws地址
            ws_url = self.ws_url  # 基础地址
            # 示例：如果需要包含服务器ID参数
            # ws_url = f"{self.ws_base_url}?server_id={self.selected_server.get('id')}"
            
            # 获取HTTP请求中的cookies
            # cookies = self.client.cookies
            cookies = self.client_cookies
            cookie_str = "; ".join([f"{k}={v}" for k, v in cookies.items()])

            logger.info(f"用户 {self.user_id} 尝试WebSocket连接{ws_url}")
            self.ws_send_time = 0
            self.ws_start_time = time.time()
            # 连接WebSocket
            self.ws = websocket.WebSocketApp(
                ws_url,
                cookie="",
                on_open=self.on_ws_open,
                on_message=self.on_ws_message,
                on_error=self.on_ws_error,
                on_close=self.on_ws_close
            )
            
            # 启动WebSocket线程
            self.ws_thread = threading.Thread(target=self.ws.run_forever)
            self.ws_thread.daemon = True
            self.ws_thread.start()
            
            # 等待连接建立
            start_time = time.time()
            while not self.connected and time.time() - start_time < 10:
                time.sleep(0.1)
            
            if not self.connected:
                logger.error(f"用户 {self.user_id} WebSocket连接超时")
                raise Exception("WebSocket连接超时")
                
            logger.info(f"用户 {self.user_id} 成功连接到服务器WebSocket")

            time.sleep(0.5)
            # 发送初始连接消息，可包含选中的服务器信息
            self.send_initial_message()
            
        except Exception as e:
            logger.error(f"用户 {self.user_id} WebSocket连接失败: {str(e)}")
            raise

    def on_ws_open(self, ws):
        """WebSocket连接打开时回调"""
        self.connected = True
        logger.info(f"用户 {self.user_id} WebSocket连接已打开")
        
        # 记录连接建立时间
        self.connection_established_time = time.time()
        events.request.fire(
            request_type="WS",
            name="websocket连接",
            response_time=(self.connection_established_time - self.ws_start_time) * 1000,
            response_length=0,
            exception=None,
        )

        # # 发送初始连接消息，可包含选中的服务器信息
        # self.send_initial_message()

    def on_ws_message(self, ws, message):
        """收到WebSocket消息时回调"""
        
        
        temp_send_time = self.ws_send_time
        if temp_send_time != 0:
            self.ws_recv_time = time.time()
            # logger.info(f"用户 {self.user_id} 收到消息: {temp_send_time}")

            # 记录接收消息事件
            events.request.fire(
                request_type="WS_RECV",
                name="ws接收消息",
                response_time=(self.ws_recv_time - temp_send_time) * 1000,
                response_length=len(message),
                exception=None  # 成功时传None
            )
            self.ws_send_time = 0

        self.handle_recv(message)

        while self.recv_buffers:
            curmsg = self.recv_buffers.pop(0)
            # 处理收到的消息
            self.handle_message(curmsg)

    def handle_recv(self, data):
        """
        处理接收到的二进制数据
        :param data: 字节流数据
        """
        read_len = 0
        data_length = len(data)
        
        while read_len < data_length:
            # 检查是否有足够的字节读取长度和类型
            if read_len + 6 > data_length:  # int(4字节) + short(2字节) = 6字节
                print(f"recv illData: 数据不足，需要至少6字节，剩余{data_length - read_len}字节")
                break
                
            # 读取长度（4字节，小端模式）
            len_bytes = data[read_len:read_len+4]
            length = struct.unpack('<i', len_bytes)[0]
            read_len += 4
            
            # 读取类型（2字节，小端模式）
            type_bytes = data[read_len:read_len+2]
            type_ = struct.unpack('<h', type_bytes)[0]
            read_len += 2
            
            # 检查长度是否合法
            if length < 18:
                print(f"recv illData Len: {length}")
                break
                
            # 检查是否有足够的字节读取完整包
            if read_len + (length - 6) > data_length:  # 减去已读取的6字节
                print(f"recv illData: 数据包不完整，需要{length}字节，剩余{data_length - read_len}字节")
                break
                
            # 读取整个数据包（包括已读取的6字节）
            # 从当前位置向前回退6字节，读取完整的length长度数据
            start_pos = read_len - 6
            packet_data = data[start_pos:start_pos + length]
            read_len += (length - 6)  # 加上剩余需要读取的字节数
            
            # 将数据包添加到缓冲区
            self.recv_buffers.append(packet_data)
        
        # 检查是否所有数据都被正确解析
        if read_len != data_length:
            print(f"recv error Len: {read_len} all len {data_length}")

    def on_ws_error(self, ws, error):
        """WebSocket错误时回调"""
        logger.error(f"用户 {self.user_id} WebSocket错误: {str(error)}")
        
        # 尝试重新连接
        # if not self.connected:
        #     logger.info(f"用户 {self.user_id} 尝试重新连接WebSocket")
        #     self.connect_websocket()

    def on_ws_close(self, ws, close_status_code, close_msg):
        """WebSocket关闭时回调"""
        self.connected = False
        logger.info(f"用户 {self.user_id} WebSocket连接已关闭: {close_status_code} - {close_msg}")
        
        # 尝试重新连接
        logger.info(f"用户 {self.user_id} 尝试重新连接WebSocket")
        self.connect_websocket()

    def insert_in_binary(self, original_data, position, new_data):
        """
        在二进制数据的指定位置插入新数据
        
        参数:
            original_data: 原始二进制数据 (bytes类型)
            position: 插入位置 (字节偏移量)
            new_data: 要插入的新二进制数据 (bytes类型)
        
        返回:
            插入后的二进制数据 (bytes类型)
        """
        # 将bytes转换为列表以便修改
        data_list = list(original_data)
        new_data_list = list(new_data)
        
        # 计算修改的结束位置
        end_position = position + len(new_data_list)
        
        # 确保修改范围有效（不超出原始数据长度）
        if position < 0 or end_position > len(data_list):
            raise ValueError("修改范围超出原始数据长度")
        
        # 用新数据覆盖指定位置的内容
        data_list[position:end_position] = new_data_list
        
        # 转回bytes类型并返回
        return bytes(data_list)
    
    def send_initial_message(self):
        """发送初始连接消息"""
        isFirst = 0 #1 if self.isFirstLogin else 0
        loginParam="connect_0|Device_1|isFirstLogin_"+str(isFirst)+"|ipaddr_127.0.0.1|cid_1"
        opengId = self.user_id
        logger.info(f"发送初始连接消息")
        msg = self.send_login_to_server(loginParam, opengId)
        
        self.send_message(msg)

    def send_login_to_server(self, login_param, open_id):
        """
        转换自JavaScript的__proto.SendLoginToServer函数
        :param login_param: 登录参数
        :param open_id: 开放平台ID
        :return: 构建好的登录消息对象
        """
        msg = SendLoginAccount()
        msg.sType = 20005  # 假设登录消息类型为20005
        MConfig = {
            "GateSessionnKey": "0_0",  # 注意原JS中是GateSessionnKey（可能多了一个n）
            "ServerId": 5,
            "ParamType": 0,
            "RoleIndex": "0_0"
        }
        
        msg.GateSocket = MConfig["GateSessionnKey"]
        msg.Int64Ex = "0_0"
        
        # 处理accoutSessionKey（原JS中有拼写错误）
        if len(open_id) > 32:
            # 截取最后32个字符
            msg.accoutSessionKey = open_id[-32:]
        else:
            msg.accoutSessionKey = open_id
        
        msg.worldServerId = self.server_no #MConfig["ServerId"]
        sn_num = 0
        sb = ""
        check_token_string = msg.accoutSessionKey
        
        # 处理检查令牌字符串
        if len(check_token_string) < 10:
            sb += check_token_string
            # 不足10位则补数字
            for i in range(len(check_token_string), 10):
                sb += str(i)
            check_token_string = sb
        
        # 计算sn_num
        for char in check_token_string:
            item_num = ord(char)  # 获取字符的ASCII码
            
            # 根据不同的取模结果调整数值
            if item_num % 9 == 0:
                item_num += 1
            elif item_num % 8 == 0:
                item_num += 2
            elif item_num % 7 == 0:
                item_num += 3
            elif item_num % 6 == 0:
                item_num += 4
            elif item_num % 5 == 0:
                item_num += 5
            elif item_num % 4 == 0:
                item_num += 6
            elif item_num % 3 == 0:
                item_num += 7
            elif item_num % 2 == 0:
                item_num += 8
            else:
                item_num += 9
            
            # 更新sn_num
            sn_num += item_num + (sn_num * item_num)
            
            # 控制sn_num的范围
            if sn_num > 89898989:
                sn_num = sn_num % 89898989 + 89898989
        
        # 设置消息其他字段
        msg.sType = 20005
        msg.ParamType = MConfig["ParamType"]
        msg.ParamInt64 = MConfig["RoleIndex"]
        msg.ParamString = f"{login_param}|Sn_{sn_num}"
        
        return msg

    def send_message(self, data, sType=0):
        """发送消息到WebSocket服务器"""
        # # 打印self（当前对象）的基本信息
        # print("===== self 对象信息 =====")
        # print(f"self 的属性值: {self.__dict__}\n")

        # # 打印data的信息
        # print("===== data 参数信息 =====")
        # print(f"data 的内容: {data.__dict__}")        # 打印具体内容

        if not isinstance(data, MsgHeader) : # 不处理
            return

        if self.connected and self.ws:
            try:
                # 为每个请求添加唯一标识
                self.request_id += 1

                start_time = time.time()

                # 按照固定格式打包（示例格式，需根据实际需求调整）
                binary_data = data.writeBinary()

                byteLen = len(binary_data)
                # logger.info(f"插入前长度: {len(binary_data)}字节")
                insert_data = struct.pack('<h', byteLen)
                # 在第4个字节位置插入（第一个整数之后）
                result = self.insert_in_binary(binary_data, 0, insert_data)
                # logger.info(f"插入后长度: {len(result)}字节")

                result += struct.pack('<i', 0)
                result += struct.pack('<h', 0)
                # 发送最终的二进制数据
                self.ws.send(result, opcode=websocket.ABNF.OPCODE_BINARY)

                # 转换为列表形式，类似JavaScript数组
                # data_list = list(result)
                # print("打包后的数据(十进制):", data_list)
                # print("打包后的数据(十六进制):", [hex(b) for b in data_list])

                response_time = int((time.time() - start_time) * 1000)

                # start_time = time.time()
                # self.ws.send(data_str)
                # response_time = int((time.time() - start_time) * 1000)
                
                if self.ws_send_time == 0:
                    self.ws_send_time = time.time()
                # 记录发送成功事件
                events.request.fire(
                    request_type="WS_SEND_BINARY",
                    name="ws发送消息",
                    response_time=response_time,
                    response_length=len(binary_data),
                    exception=None  # 成功时传None
                )
                
                # logger.info(f"用户 {self.user_id} 发送消息: id={data.sType}，长度: {len(result)}字节") #，内容{data.__dict__}
                return True
            except Exception as e:
                logger.error(f"用户 {self.user_id} 发送消息失败: {str(e)}")
                events.request.fire(
                    request_type="WS_SEND_BINARY",
                    name="发送消息",
                    response_time=int((time.time() - start_time) * 1000),
                    exception=e
                )
                return False
        else:
            logger.warning(f"用户 {self.user_id} 尝试发送消息，但WebSocket未连接")
            return False

    def handle_message(self, message):
        """处理收到的消息"""
        """
        解析二进制消息：先解析头部的Int(结构体长度)和Short(消息ID)，再根据消息ID判断结构
        
        :param data: 二进制数据（bytes类型）
        :return: 解析后的消息字典或None
        """
        try:
            data = message
            # 定义头部格式：
            # ! 表示网络字节序（大端模式）
            # I 表示4字节无符号Int（结构体总长度，包含头部）
            # H 表示2字节无符号Short（消息ID标记）
            offset = 0
            header_format = "<i"
            header_size = struct.calcsize(header_format) 
            bfLen = struct.unpack(header_format, data[offset:offset+header_size])[0] # 字节总长度
            offset += header_size

            header_format = "<H"
            header_size = struct.calcsize(header_format) 
            msg_id = struct.unpack(header_format, data[offset:offset+header_size])[0] # 消息ID
            offset += header_size
            
            # logger.info(f"用户 {self.user_id} 收到消息:ID={msg_id} 总长度={bfLen}")

            msg = None
            if msg_id == 20003 :
                msg = None #MsgAccountRole()
            elif msg_id == 32501 :
                msg = MsgAccountRole()
            elif msg_id == 32525 :
                msg = MsgGetRandName()
            elif msg_id == 32754 : #MsgTryEnterMap
                msg = MsgTryEnterMap()
                # self.is_enter_game = True
                # logger.info(f"用户 {self.user_id} 成功进入游戏地图: {self.selected_role.mName}")
            elif msg_id == 32502 :# 成功进入游戏
                self.is_enter_game = False
                # logger.info(f"用户 {self.user_id} 成功进入游戏地图: {self.selected_role.mName}")
            elif msg_id == 32751 :# 成功进入地图
                if not self.is_enter_game :
                    self.is_enter_game = True
                    logger.info(f"用户 {self.user_id} 成功进入游戏地图: {self.selected_role.mName}")
            elif msg_id == 39751 :
                msg = MsgPing()
            if msg :
                msg.readBinary(data)
                # logger.info(f"用户 {self.user_id} 解析消息成功: {msg_id}")#，内容{msg.__dict__}
                self.exe_message_logic(msg)
                
        except struct.error as e:
            logger.error(f"解析二进制消息失败: {str(e)}")
            return None
        
    def exe_message_logic(self, msg):
        """根据消息内容执行相应的逻辑"""
        if isinstance(msg, MsgAccountRole):
            logger.info(f"用户 {self.user_id} 处理角色列表消息，角色数: {len(msg.arrRoles)}")
            # 可以在这里添加更多逻辑处理
            # 例如选择一个角色登录游戏等
            role:MsgAccountRole = msg
            if len(role.arrRoles) > 0:
                selected_role:NetChar = role.arrRoles[-1] # 选择第一个角色
                logger.info(f"用户 {self.user_id} 选择角色: {selected_role.mName}")

                self.selected_role = selected_role
                self.send_enter_game(selected_role.RoleIndex, selected_role.mName)
            else :
                random_name:CsgGetRandName = CsgGetRandName()
                random_name.sType = 32009
                random_name.Sex = 1 # 1男 2女
                self.send_message(random_name)
        elif isinstance(msg, MsgGetRandName):
            logger.info(f"用户 {self.user_id} 处理随机名字消息，名字: {msg.RandName}")
            self.send_create_role(msg.RandName)

        elif isinstance(msg, MsgTryEnterMap):
            self.mMsgTryEnterMap = msg
            self.send_map_begin(self.selected_role.RoleIndex, self.selected_role.mName)

        elif isinstance(msg, MsgPing):
            ping:MsgPing = msg
            self.LastPingTime = math.floor(ping.LastPingTime)
            # logger.info(f"用户 {self.user_id} 心跳返回: {self.LastPingTime}")

    def send_enter_game(self, role_index, name):
        """发送进入游戏消息"""

        time.sleep(0.5)

        logger.info(f"用户 {self.user_id} 发送进入游戏消息: {role_index}，name={name}")
        msg = CsgEnterWorld()
        msg.RoleIndex = role_index
        self.send_message(msg)

        time.sleep(0.5)

        self.send_game_begin(role_index, name)
        
    def send_game_begin(self, role_index, name):
        """发送 前端准备好了 消息"""
        logger.info(f"用户 {self.user_id} 发送 代码准备好了 消息: {role_index}，name={name}")
        msg = CsgFinishLoadGameMain()
        msg.RoleIndex = role_index
        self.send_message(msg)


    def send_map_begin(self, role_index, name):
        time.sleep(1.5)

        logger.info(f"用户 {self.user_id} 发送 地图准备好了 消息: {role_index}，name={name}")
        finishMap = CsgFinishLoadMap()
        finishMap.MapIndex = self.mMsgTryEnterMap.MapIndex
        finishMap.MapId = self.mMsgTryEnterMap.MapId
        self.send_message(finishMap)

        time.sleep(0.1)

        logger.info(f"用户 {self.user_id} 发送 请求进入游戏 消息: {role_index}，name={name}")
        enterGame = CsgEnterGameMain()
        self.send_message(enterGame)

    def send_create_role(self, name, job=0, sex=1):
        """发送创建角色消息"""
        logger.info(f"用户 {self.user_id} 发送创建角色消息: {name}")
        msg = CsgCreateRole()
        msg.newRole.mName = name
        msg.newRole.mJob = job
        msg.newRole.mGender = sex
        self.send_message(msg)

    @task(1)
    def send_heartbeat(self):
        """发送心跳包任务"""
        if self.connected and self.is_enter_game == True :
            # logger.info(f"用户 {self.user_id} 发送心跳包任务: {self.LastPingTime}")
            if self.LastPingTime is None:
                self.LastPingTime = 0
            ping = CsgPing()
            ping.sType = 39701
            ping.LastPingTime = self.LastPingTime
            self.send_message(ping)

    @task(2)
    def send_game_action(self):
        """发送游戏操作任务"""
        if self.connected and self.is_enter_game == True :
            msg = CMgSignOpen()
            self.send_message(msg)

            #随机走动下,203,82
            if not self.pos_vector:
                self.pos_vector = [203, 82]
            random_dir = random.randint(0, 7)
            vector = self.get_pos_arr_by_dir(random_dir)
            self.pos_vector[0] += vector[0]
            self.pos_vector[1] += vector[1]

            movemsg = CsgMoveHero()
            movemsg.posx = self.pos_vector[0]
            movemsg.posy = self.pos_vector[1]
            movemsg.dir = random_dir
            movemsg.Time = int(time.time() * 1000)
            movemsg.MapId = self.mMsgTryEnterMap.MapId

            # logger.info(f"用户 {self.user_id} 发送 角色移动: {movemsg.__dict__}")
            self.send_message(movemsg)

    def get_pos_arr_by_dir(self, dir: int) -> list:
        """根据方向0-7获取向量"""
        DIR_WALK_X = [0, 1, 1, 1, 0, -1, -1, -1]
        DIR_WALK_Y = [-1, -1, 0, 1, 1, 1, 0, -1]
        
        if dir < 0 or dir > 7:
            return [0, 0]
        return [DIR_WALK_X[dir], DIR_WALK_Y[dir]]

    def on_stop(self):
        """用户结束时执行，关闭连接"""
        logger.info(f"用户 {self.user_id} 结束测试")
        if self.ws:
            # 发送断开连接消息
            self.send_message({
                "type": "disconnect",
                "timestamp": int(time.time())
            })
            time.sleep(0.1)
            self.ws.close()
        if self.ws_thread and self.ws_thread.is_alive():
            self.ws_thread.join(timeout=5)


